package com.devenv.service;

import com.devenv.common.PageRequest;
import com.devenv.common.PageResult;
import com.devenv.common.ErrorCode;
import com.devenv.dto.SoftwareDTO;
import com.devenv.dto.SoftwareVersionDTO;
import com.devenv.entity.Software;
import com.devenv.entity.SoftwareVersion;
import com.devenv.exception.BusinessException;
import com.devenv.repository.SoftwareRepository;
import com.devenv.repository.SoftwareVersionRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 软件管理服务
 * 提供基础的软件管理功能
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SoftwareService {

    private final SoftwareRepository softwareRepository;
    private final SoftwareVersionRepository versionRepository;

    /**
     * 分页查询软件列表
     */
    public PageResult<SoftwareDTO> findByPage(PageRequest pageRequest, String category, String os, String keyword) {
        log.debug("分页查询软件列表: page={}, category={}, os={}, keyword={}", 
                 pageRequest.getCurrent(), category, os, keyword);

        Pageable pageable = pageRequest.toPageable();
        Page<Software> page;

        // 根据查询条件选择不同的查询方法
        if (StringUtils.hasText(keyword)) {
            page = softwareRepository.searchByKeywordWithPage(keyword, category, os, pageable);
        } else if (StringUtils.hasText(category) || StringUtils.hasText(os)) {
            page = softwareRepository.findByCategoryAndOsWithPage(category, os, pageable);
        } else {
            page = softwareRepository.findAllByStatusOrderByDownloadCountDesc(Software.SoftwareStatus.ACTIVE, pageable);
        }

        List<SoftwareDTO> dtoList = page.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        return PageResult.of(dtoList, page.getTotalElements(), 
                           pageRequest.getCurrent(), pageRequest.getSize());
    }

    /**
     * 获取所有软件列表
     */
    public List<SoftwareDTO> findAll() {
        log.debug("获取所有软件列表");
        List<Software> softwareList = softwareRepository.findAllByStatusOrderByDownloadCountDesc(Software.SoftwareStatus.ACTIVE);
        return softwareList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 获取所有活跃软件列表
     */
    public List<Software> findAllActive() {
        log.debug("获取所有活跃软件列表");
        return softwareRepository.findAllByStatusOrderByDownloadCountDesc(Software.SoftwareStatus.ACTIVE);
    }

    /**
     * 根据ID获取软件详情
     */
    public SoftwareDTO findById(@NotNull Long id) {
        log.debug("获取软件详情: id={}", id);
        return softwareRepository.findById(id)
                .filter(software -> !software.getDeleted())
                .map(this::convertToDetailDTO)
                .orElseThrow(() -> new BusinessException(ErrorCode.SOFTWARE_NOT_FOUND, "软件ID: " + id));
    }

    /**
     * 根据分类获取软件列表
     */
    public List<SoftwareDTO> findByCategory(@NotNull String category) {
        log.debug("根据分类获取软件列表: category={}", category);
        List<Software> softwareList = softwareRepository.findByCategoryAndStatusOrderByDownloadCountDesc(
                category, Software.SoftwareStatus.ACTIVE);
        return softwareList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据操作系统支持获取软件列表
     */
    public List<SoftwareDTO> findByOsSupport(@NotNull String os) {
        log.debug("根据操作系统获取软件列表: os={}", os);
        List<Software> softwareList = softwareRepository.findByOsContainingAndStatusOrderByPriorityDesc(
                os, "ACTIVE");
        return softwareList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 关键词搜索软件
     */
    public List<SoftwareDTO> searchByKeyword(@NotNull String keyword) {
        log.debug("搜索软件: keyword={}", keyword);
        List<Software> softwareList = softwareRepository.searchByKeyword(keyword);
        return softwareList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 获取所有分类
     */
    public List<String> getAllCategories() {
        log.debug("获取所有软件分类");
        return softwareRepository.findAllCategories();
    }

    /**
     * 获取软件版本列表
     */
    public List<SoftwareVersionDTO> getVersionsBySoftwareId(@NotNull Long softwareId) {
        log.debug("获取软件版本列表: softwareId={}", softwareId);
        List<SoftwareVersion> versions = versionRepository.findBySoftwareIdOrderByReleaseDateDesc(softwareId);
        return versions.stream()
                .map(this::convertVersionToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 增加下载次数
     */
    @Transactional
    public void incrementDownloadCount(@NotNull Long id) {
        log.debug("增加软件下载次数: id={}", id);
        Software software = softwareRepository.findById(id)
                .orElseThrow(() -> new BusinessException(ErrorCode.SOFTWARE_NOT_FOUND, "软件ID: " + id));
        
        software.setDownloadCount(software.getDownloadCount() + 1);
        softwareRepository.save(software);
    }

    // === 管理员功能 ===

    /**
     * 创建软件
     */
    @Transactional
    public SoftwareDTO createSoftware(@NotNull SoftwareDTO softwareDTO) {
        log.debug("创建软件: name={}", softwareDTO.getName());
        
        // 检查名称是否重复
        if (softwareRepository.existsByName(softwareDTO.getName())) {
            throw new BusinessException(ErrorCode.SOFTWARE_NAME_EXISTED, "软件名称已存在: " + softwareDTO.getName());
        }

        Software software = convertToEntity(softwareDTO);
        software = softwareRepository.save(software);
        return convertToDTO(software);
    }

    /**
     * 更新软件
     */
    @Transactional
    public SoftwareDTO updateSoftware(@NotNull Long id, @NotNull SoftwareDTO softwareDTO) {
        log.debug("更新软件: id={}, name={}", id, softwareDTO.getName());
        
        Software existingSoftware = softwareRepository.findById(id)
                .orElseThrow(() -> new BusinessException(ErrorCode.SOFTWARE_NOT_FOUND, "软件ID: " + id));

        // 检查名称是否被其他软件使用
        if (!existingSoftware.getName().equals(softwareDTO.getName()) && 
            softwareRepository.existsByName(softwareDTO.getName())) {
            throw new BusinessException(ErrorCode.SOFTWARE_NAME_EXISTED, "软件名称已存在: " + softwareDTO.getName());
        }

        BeanUtils.copyProperties(softwareDTO, existingSoftware, "id", "createdAt", "createdBy");
        Software savedSoftware = softwareRepository.save(existingSoftware);
        return convertToDTO(savedSoftware);
    }

    /**
     * 删除软件（逻辑删除）
     */
    @Transactional
    public void deleteSoftware(@NotNull Long id) {
        log.debug("删除软件: id={}", id);
        
        Software software = softwareRepository.findById(id)
                .orElseThrow(() -> new BusinessException(ErrorCode.SOFTWARE_NOT_FOUND, "软件ID: " + id));
        
        software.setDeleted(true);
        softwareRepository.save(software);
    }

    // === 转换工具方法 ===

    private SoftwareDTO convertToDTO(Software software) {
        SoftwareDTO dto = new SoftwareDTO();
        BeanUtils.copyProperties(software, dto);
        dto.setStatus(software.getStatus().name());
        return dto;
    }

    private SoftwareDTO convertToDetailDTO(Software software) {
        SoftwareDTO dto = convertToDTO(software);
        
        // 加载版本信息
        List<SoftwareVersionDTO> versions = getVersionsBySoftwareId(software.getId());
        dto.setVersions(versions);
        dto.setVersionCount(versions.size());
        
        if (!versions.isEmpty()) {
            dto.setLatestVersion(versions.get(0)); // 第一个是最新版本
        }
        
        return dto;
    }

    private Software convertToEntity(SoftwareDTO dto) {
        Software software = new Software();
        BeanUtils.copyProperties(dto, software, "id", "status");
        
        if (StringUtils.hasText(dto.getStatus())) {
            software.setStatus(Software.SoftwareStatus.valueOf(dto.getStatus()));
        }
        
        return software;
    }

    private SoftwareVersionDTO convertVersionToDTO(SoftwareVersion version) {
        SoftwareVersionDTO dto = new SoftwareVersionDTO();
        BeanUtils.copyProperties(version, dto);
        return dto;
    }
}
